--mt_api_addons
--License: MIT License

--Event registration lists
minetest.registered_removeentity_events = {}
minetest.registered_clear_objects_events = {}
minetest.registered_playerstep_events = {}

--Hooks a function onto a function
local function hook(func, func2)
  if type(func) == "function" then
    return function(...)
      func2(...)
      return func(...)
    end
  end
  return nil
end

--Handler needed for specific events
local function do_handler(category, ...)
  local events = minetest["registered_"..category.."_events"]
  if events then
    for count,func in pairs(events) do
      func(...)
    end
  end
end

--Handle playerstep events
local function handle_playerstep_events(dtime)
  local events = minetest.registered_playerstep_events
  if events then
    for _,player in pairs(minetest.get_connected_players()) do
      for count,event in pairs(events) do
        local name = player:get_player_name()
        local interval = event.interval
        if not minetest.registered_playerstep_events[count].players[name] then
          minetest.registered_playerstep_events[count].players[name] = {time = 0}
        end
        local time = minetest.registered_playerstep_events[count].players[name].time
        time = time + dtime
        if time >= interval then
          event.func(player, dtime)
          minetest.registered_playerstep_events[count].players[name].time = 0
        else
          minetest.registered_playerstep_events[count].players[name].time = time
        end
      end
    end
  else
    return  
  end
end

--Use globalstep to handle playerstep events
minetest.register_globalstep(function(dtime)
  handle_playerstep_events(dtime)
end)

--Safely injects a hook to builtin function clear_objects
core.clear_objects = hook(core.clear_objects, function(option)
  do_handler("clear_objects", option)
end)

--Safely Inject hooks to all registered entities after all mods have been loaded
minetest.register_on_mods_loaded(function(player)
  for name,def in pairs(minetest.registered_entities) do
    minetest.registered_entities[name].get_staticdata = hook(def.get_staticdata, function(self)
      if not self.hook_injected then
        local meta = getmetatable(self.object)
        meta.remove = hook(meta.remove, function(self)
          --get entity properties
          self = self:get_luaentity()
          if not(self.removed or self.removed == true) then
            self.removed = true
            do_handler("removeentity", self)
          end
        end)
        --Update objectref's Metatable
        setmetatable(meta, meta)
      end
    end)
  end
  minetest.log("action", "Entity on_remove hook injected successfully.")
end)

--Backwards compatability substitute for on_rightclickplayer (for those who are using outdated clients)
if not minetest.register_on_rightclickplayer then
  local update_interval = minetest.settings:get("player_rightclick_step") or 0.1
  minetest.registered_rightclickplayer_events = {}

  --Check if the player right-clicked a player
  local function verify_clicked_player(clicker, range)
    local result = nil
    local ray_start = vector.add(clicker:get_pos(), {x=0, y= clicker:get_properties().eye_height, z=0})
    local ray_modif = vector.add(vector.multiply(clicker:get_look_dir(), range), clicker:get_eye_offset())
    local ray_end = vector.add(ray_start, ray_modif)
    local ray = minetest.raycast(ray_start, ray_end, true, false)
    local clicked = ray:next()
    clicked = ray:next()
    if clicked and clicked.type == "object" and clicked.ref:is_player() and clicked.ref ~= clicker then
      result = clicked.ref
    end
    return result
  end

  --Handle rightclicking for older minetest versions
  local function handle_rightclickplayer()
    local events = minetest.registered_rightclickplayer_events
    if events then
      for _,clicker in pairs(minetest.get_connected_players()) do
        local right_click = clicker:get_player_control().RMB or false
        if clicker and right_click == true then
          local name = clicker:get_player_name()
          local clicked = verify_clicked_player(clicker, 4)
          if clicked then
            for _,func in pairs(events) do 
              func(clicker, clicked)
            end  
          end
          break
        end
      end 
      minetest.after(update_interval, function()
        handle_rightclickplayer()
      end)
    end
  end
  
  --Register on rightclick player (Called when a player rightclicked a fellow player)
  function minetest.register_on_rightclickplayer(func)
    assert(type(func) == "function", "you must include a function with the following arguements (player, clicker).")
    table.insert(minetest.registered_rightclickplayer_events, func)
  end
end

--Register on playerstep (Just like globalstep, but specifically for players. interval included for convenience and performance reasons)
function minetest.register_playerstep(interval, func)
  assert(interval and type(interval) == "number", "must include a interval number value.")
  assert(func and type(func) == "function", "must include a function with the following arguements (dtime).")
  table.insert(minetest.registered_playerstep_events, {["players"] = {}, ["interval"] = interval, ["func"] = func})
end

--Register on remove entity (Called after objectref:remove() gets called)
function minetest.register_on_removeentity(func)
  assert(type(func) == "function", "you must include a function with the following arguement (self).")
  table.insert(minetest.registered_removeentity_events, func)
end

--Register on remove entity (Called when builtin core.clear_objects() gets called)
function minetest.register_on_clearobjects(func)
  assert(type(func) == "function", "you must include a function with the following arguement (option).")
  table.insert(minetest.registered_clear_objects_events, func)
end